# This file is part of the AutoMAT distribution (https://bitbucket.com/mahomaho/AutoMAT).
# Copyright (c) 2020 Mattias Holmqvist.
# 
# AutoMAT is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
# 
# AutoMAT is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with AutoMAT.  If not, see <https://www.gnu.org/licenses/>.

from collections import defaultdict, OrderedDict
import os
import signal
import sys

import argparse
import shlex

import time
start_time = time.time()

class MyArgumentParser(argparse.ArgumentParser):
	# split arguments in the file
    def convert_arg_line_to_args(self, arg_line):
        return shlex.split(arg_line)
parser = MyArgumentParser(description='AutoMat arxml script tool', fromfile_prefix_chars='@')
parser.add_argument('-s', '--scripts', nargs='*', help='script files to be executed. It is also possible to pass arguments to the script. To do so, surround the script and arguments with quotations', default=[])

parser.add_argument('-d', '--debug', action='store_true', help='debug mode, add more trace output e.g. call stack in case of segmentation fault', default=False)
parser.add_argument('-v', '--verbose', action='store_true', help='enable debug printouts', default=False)
parser.add_argument('-e', '--stop_on_error', action='store_true', help='return on first script error instead of finish all scripts', default=False)
parser.add_argument('-p', '--extra_paths', nargs='*', help='directories to be appended to the path environment variable when scripts are executed', default=[])
parser.add_argument('-P', '--profile', action='store_true', help='profile scripts and store results in <scripts>.profile', default=False)

parser.add_argument('-a', '--arxml', nargs='*', help='individual arxml files to load', default=[])
parser.add_argument('-l', '--dirs', nargs='*', help='dirs to search for arxmls to load', default=[])
parser.add_argument('-L', '--basedirs', nargs='*', help='dirs to search for arxmls recursively for files to load', default=[])

parser.add_argument('-R', '--autosar_release', choices=['AUTOSAR_4-2-2.xsd', 'AUTOSAR_00046.xsd', 'AUTOSAR_00048.xsd', 'AUTOSAR_00049.xsd', 'AUTOSAR_00052.xsd'], help='AUTOSAR schema version')

args = parser.parse_args()

if args.autosar_release:
	os.environ['AUTOSAR_RELEASE']=args.autosar_release[:-4]
# make sure that the loader path is included in the system path to make it possible to load AutoMAT
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
from AutoMAT import *

if args.debug:
	import faulthandler
	faulthandler.enable()

if args.verbose:
	import AutoMAT
	AutoMAT._vprint=print
from AutoMAT import _vprint as vprint

nonexisting = [arxml for arxml in args.arxml if not os.path.isfile(arxml)]
for arxml in nonexisting:
	vprint(f'Specified file {arxml} does not exist\n')
	args.arxml.remove(arxml)

for dir in args.dirs:
	try:
		files = (os.path.join(dir, name) for name in os.listdir(dir) if name.endswith(".arxml"))
		args.arxml.extend(files)
	except FileNotFoundError:
		vprint(f'Specified path {dir} does not exist\n')
for dir in args.basedirs:
	files = (os.path.join(root, name) for root, dirs, files in os.walk(dir) for name in files if name.endswith(".arxml"))
	args.arxml.extend(files)

args.arxml.sort()
for arxml in args.arxml:
	LoadFile(arxml)
	
sys.path.append('')
# add the extra paths
for path in args.extra_paths:
	assert os.path.isdir(path), f'The specified path {path} is not a valid path'
	sys.path.append(os.path.abspath(path))
	
import traceback
numerrs=0
exitcode=0
for script in args.scripts:
	if exitcode and args.stop_on_error:
		break
	try:
		vprint('execute :' + script)
		script = shlex.split(script)
		scriptpath=os.path.abspath(script[0])
		basepath = os.path.splitext(scriptpath)[0]
		scriptname=os.path.basename(basepath)
		sys.path.append(os.path.dirname(scriptpath))
		from importlib.machinery import EXTENSION_SUFFIXES
		for suffix in EXTENSION_SUFFIXES:
			if os.path.exists(basepath+suffix):
				scriptpath = basepath+suffix
				from importlib.machinery import ExtensionFileLoader
				fileLoader = ExtensionFileLoader
				break
		else:
			from importlib.machinery import SourceFileLoader
			fileLoader = SourceFileLoader
		if args.profile:
			import cProfile
			profile=cProfile.Profile()
			profile.enable()
		mymodule = fileLoader(scriptname, scriptpath).load_module()
		if hasattr(mymodule,'main'):
			ret=mymodule.main(script[1:])
			#if isinstance(ret,int) and ret > 0:
			if ret:
				sys.stdout.flush()
				print(f'Error: Script {scriptname} exited with error code: {ret}\n', file=sys.stderr)
				numerrs+=1
				if exitcode == 0:
					if isinstance(ret,int):
						exitcode = ret
					else:
						exitcode = 1
	except SystemExit as e:
		if e.code:
			sys.stdout.flush()
			print(f'Error: Script {scriptname} exited with error code: {e.code}\n', file=sys.stderr)
			numerrs+=1
			if exitcode == 0:
				exitcode = e.code
	except Exception as e:
		sys.stdout.flush()
		print(f'Script {scriptname} exited with error:', file=sys.stderr)
		if args.verbose:
			print(traceback.format_exc(), file=sys.stderr)
		else:
			print(str(e), file=sys.stderr)
		numerrs+=1
		if exitcode==0:
			exitcode=1
	if args.profile:
		import cProfile, pstats, io
		from pstats import SortKey
		profile.disable()
		s = io.StringIO()
		sortby = SortKey.CUMULATIVE
		ps = pstats.Stats(profile, stream=s).sort_stats(sortby)
		ps.print_stats()
		with open(script[0]+'.profile', 'w') as f:
			f.write(s.getvalue())
			vprint(s.getvalue())
	try:
		sys.path.remove(os.path.dirname(scriptpath))
		del sys.modules[scriptname]
	except: pass
	sys.stdout.flush()

if exitcode!=0:
	sys.exit(exitcode)
	
vprint("all scripts run, total execution time was %s seconds" % (time.time() - start_time))
sys.exit(exitcode)
